﻿using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Windows;
using System.Windows.Controls.Primitives;
using Pfz.Factoring;

namespace Pfz.WpfControls
{
	/// <summary>
	/// This class contains the dependency properties of the generic 
	/// PropertyBoundControl and some methods to discover if a DataSource
	/// is read-only.
	/// </summary>
	public abstract class BoundControl:
		ContentCreatorControl
	{
		#region Static Area
			#region Static Constructor
				[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
				static BoundControl()
				{
					var entryAssembly = Assembly.GetEntryAssembly();
					if (entryAssembly != null)
					{
						var referencedAssemblies = entryAssembly.GetReferencedAssemblies();
						foreach(var referencedAssembly in referencedAssemblies)
							Assembly.Load(referencedAssembly);
					}

					foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
					{
						foreach(var type in assembly.GetTypes())
						{
							if (!typeof(IAutoRegisterIsDataSourceReadOnlyFunction).IsAssignableFrom(type))
								continue;

							ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
							if (constructor == null)
								continue;

							var registerer = (IAutoRegisterIsDataSourceReadOnlyFunction)constructor.Invoke(null);
							RegisterIsDataSourceReadOnlyFunction(registerer.DataSourceType, registerer.IsReadOnly, registerer.CanUseForSubTypes);
						}
					}
				}
			#endregion
		#endregion
		#region IsReadOnlyProperty
			/// <summary>
			/// Gets or sets a value indicating if the control is read-only.
			/// </summary>
			public static readonly DependencyProperty IsReadOnlyProperty =
				TextBoxBase.IsReadOnlyProperty.AddOwner
				(
					typeof(BoundControl), 
					new FrameworkPropertyMetadata
					(
						false, 
						FrameworkPropertyMetadataOptions.Inherits, 
						new PropertyChangedCallback(_OnIsReadOnlyChanged)
					)
				);

			/// <summary>
			/// Gets or sets a value indicating if this control is read-only.
			/// </summary>
			[DefaultValue(false)]
			public bool IsReadOnly
			{
				get
				{
					return GetIsReadOnly(this);
				}
				set
				{
					SetIsReadOnly(this, value);
				}
			}

			/// <summary>
			/// Gets a value indicating if the control is read-only.
			/// </summary>
			public static bool GetIsReadOnly(UIElement element)
			{
				if (element == null)
					throw new ArgumentNullException("element");

				return (bool)element.GetValue(IsReadOnlyProperty);
			}

			/// <summary>
			/// Sets a value indicating if the control is read-only.
			/// </summary>
			public static void SetIsReadOnly(UIElement element, bool value)
			{
				if (element == null)
					throw new ArgumentNullException("element");

				element.SetValue(IsReadOnlyProperty, value);
			}

			private static void _OnIsReadOnlyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
			{
				var control = (BoundControl)sender;
				control.OnIsReadOnlyChanged();
			}

			/// <summary>
			/// Implemented by child-controls to change its visual state when IsReadOnly is changed.
			/// </summary>
			protected abstract void OnIsReadOnlyChanged();
		#endregion

		#region IsDataSourceReadOnly
			/// <summary>
			/// Checks if the given data-source is read-only.
			/// Will use the registered functions for validation.
			/// </summary>
			public static bool IsDataSourceReadOnly(object dataSource)
			{
				if (dataSource == null)
					return true;
				
				var function = _isDataSourceReadOnlyFunctions.FindUpOrDefault(dataSource.GetType());
				if (function == null)
					return false;
				
				return function(dataSource);
			}
		#endregion
		#region RegisterIsReadOnlyFunction
			private static TypeDictionary<Func<object, bool>> _isDataSourceReadOnlyFunctions = new TypeDictionary<Func<object, bool>>();
		
			/// <summary>
			/// Registers a function to detect if a data-source is read-only.
			/// </summary>
			public static void RegisterIsDataSourceReadOnlyFunction(Type objectType, Func<object, bool> function)
			{
				RegisterIsDataSourceReadOnlyFunction(objectType, function, false);
			}

			/// <summary>
			/// Registers a function to detect if a data-source is read-only.
			/// </summary>
			public static void RegisterIsDataSourceReadOnlyFunction(Type objectType, Func<object, bool> function, bool canUseForSubTypes)
			{
				_isDataSourceReadOnlyFunctions.Set(objectType, function, canUseForSubTypes);
			}
		#endregion
	}

}
